import struct
import chunk
import collections
import StringIO

Header = collections.namedtuple('HeaderChunk', 'format tracks ticks')
TrackEvent = collections.namedtuple('TrackEvent', 'delta event')
Format = ('single track', 'multi-track (sync)', 'multi-track (async)')

def read_varlen(stream):
    value = ord(stream.read(1)) & 0x80
    if value:
        value &= 0x7f
        while True:
            c = ord(stream.read(1)) & 0x7f
            value = (value << 7) + c
            if c & 0x80:
                break
    return value

def midi_decode_header(chk):
    if chk.getname() != 'MThd':
        raise IOError, 'Not MIDI (MThd error)'
    data = struct.unpack('>HHH', chk.read())
    header = Header._make(data)
    return header

def midi_decode_track(chk):
    if chk.getname() != 'MTrk':
        raise IOError, 'Not MIDI (MTrk error)'
    data = StringIO.StringIO(chk.read())
    delta = read_varlen(data)
    return TrackEvent._make((delta, data))

def midi_decode(stream):
    chk = chunk.Chunk(stream, align=False)
    hdr = midi_decode_header(chk)
    num_tracks = hdr.tracks
    tracks = []
    while num_tracks > 0:
        chk = chunk.Chunk(stream, align=False)
        data = midi_decode_track(chk)
        tracks.append(data)
        num_tracks -= 1
    return hdr, tracks

def midi_info(stream):
    hdr, data = midi_decode(stream)
    print 'MIDI %s, %s tracks, %s ticks.' % (Format[hdr.format],
        hdr.tracks, hdr.ticks)
    for track in range(hdr.tracks):
        print track+1, tuple(data[track].event.read())[0:8]
